﻿Imports Kairyu.DCEngine

''' <summary>
''' テーブルマップ生成クラス
''' </summary>
''' <remarks></remarks>
Friend Class TableMapFactory

    ''' <summary>
    ''' テーブルインデックス
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Private Property Index As Integer = 0

    ''' <summary>
    ''' クエリ
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Private Property Query As IQuery

    ''' <summary>
    ''' 外部結合を認めるかどうかを示します
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Private ReadOnly Property CanLeftJoin As Boolean
        Get
            Return Me.Query.CanLeftJoin
        End Get
    End Property

    ''' <summary>
    ''' 上層カスケードレベルを示します
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Private ReadOnly Property CascadeLevel As Integer
        Get
            Return Me.Query.CascadeLevel
        End Get
    End Property

    ''' <summary>
    ''' ルートテーブルマップを作成します
    ''' </summary>
    ''' <param name="root"></param>
    ''' <param name="query"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function Create(root As Type, query As IQuery) As RootTableMap
        '変数初期化
        Me.Query = query
        Me.Index = 0

        'ルートを起点に再帰処理で生成する
        Return Me.Create(Function() New RootTableMap With {.Query = Me.Query}, current:=root, lv:=0, isNullable:=False)
    End Function

    ''' <summary>
    ''' テーブルマップを作成します
    ''' </summary>
    ''' <typeparam name="T"></typeparam>
    ''' <param name="activator">テーブルマップアクティベータ</param>
    ''' <param name="current">作業対象型</param>
    ''' <param name="lv">階層。ルートは0です</param>
    ''' <param name="isNullable">外部結合かどうかを示します</param>
    ''' <returns></returns>
    ''' <remarks>再帰処理です</remarks>
    Private Function Create(Of T As {TableMap})(activator As Func(Of T), current As Type, lv As Integer, isNullable As Boolean) As T
        'インスタンス
        Dim tmap As TableMap = activator.Invoke
        tmap.Level = lv
        tmap.Index = Me.Index
        tmap.Definition = current.ToEntityDefinition

        '列マッピング追加
        Dim cindex As Integer = 0
        For Each prop In tmap.Definition.LiteralProperties
            Dim c As New ColumnMap(tmap, cindex, prop)
            'ルートでないキーは結合フラグを立て、SELECT句から除外できるようにする
            If tmap.IsRoot = False AndAlso prop.IsKey Then
                c.IsRelated = True
            Else
                c.IsRelated = False
            End If

            tmap.Columns.Add(c)
            cindex += 1
        Next

        'ユニークインデックスをインクメント
        Me.Index += 1

        '基底へカスケード
        If tmap.Definition.IsRoot = False Then
            'レベルをインクリメントせずに再帰
            Dim basemap = Me.Create(Function() New RelatedTableMap With {.RelationType = RelationTypes.Base, .IsNullable = isNullable}, tmap.Definition.BaseEntityType, lv, isNullable)

            '結合プロパティの追加
            For Each item In tmap.Definition.KeyProperties
                Dim rprop = RelatedProperty.Create(item, item)
                basemap.RelatedProperties.Add(rprop)
            Next

            '結合テーブルマップを追加
            tmap.Relations.Add(basemap)
        End If

        'レベルが上限の場合、上層カスケードせずに終了
        If tmap.Level = Me.CascadeLevel Then Return tmap

        '上限でない場合、上層へカスケード
        For Each prop In tmap.Definition.UpperProperties
            If Me.CanLeftJoin = False AndAlso prop.IsNullable Then Continue For

            '除外プロパティである場合は結合処理をしない
            If Me.Query.IgnoreJoins.Contains(prop) Then
                Continue For
            End If

            'Nullableの伝搬
            If prop.IsNullable Then isNullable = True

            'レベルをインクリメントして再帰
            Dim uppermap = Me.Create(Function() New RelatedTableMap With {.RelationType = RelationTypes.Upper, .IsNullable = isNullable}, prop.PropertyType, lv + 1, isNullable)

            '結合プロパティの追加
            For Each item In prop.ToLiteralProperties
                uppermap.RelatedProperties.Add(item)
            Next

            '結合テーブルマップを追加
            tmap.Relations.Add(uppermap)
        Next

        Return tmap
    End Function
End Class